package com.soup.common.module;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.soup.common.constant.CommonConstant;
import com.soup.common.entity.ErrorCode;
import com.soup.common.entity.Result;
import com.soup.common.exception.ServiceException;
import com.soup.common.service.CacheService;
import com.soup.common.utils.FileUtil;
import com.soup.common.utils.RequestContextHolderUtil;
import com.soup.common.config.FileProperties;
import com.soup.common.exception.auth.AuthException;
import com.soup.common.exception.sys.FileOperationException;
import com.soup.common.module.sys.data.AuthResponse;
import com.soup.common.module.sys.data.SignInRequest;
import com.soup.common.module.sys.entity.UpFile;
import com.soup.common.module.sys.mapper.UpFileMapper;
import com.soup.common.module.sys.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;

/**
 * 常用接口
 *
 * @author zhaoyi
 */
@Slf4j
@RestController
@RequestMapping("/active")
public class ActiveApi {

    @Autowired
    private FileProperties fileProperties;

    @Autowired
    private UserService userService;

    @Resource
    private UpFileMapper upFileMapper;

    @Autowired
    private DefaultKaptcha captchaProducer;

    @Autowired
    private CacheService cacheService;

    /**
     * 登陆
     *
     * @param signInRequest 登陆信息：用户名userName，密码password
     * @return 返回登陆token
     */
    @PostMapping("/login")
    public AuthResponse login(@Valid @RequestBody SignInRequest signInRequest) {
        // 校验图片验证码
        String capText = cacheService.get(CommonConstant.KAPTCHA_CACHE, signInRequest.getCaptchaKey());
        if (!StringUtils.equalsIgnoreCase(capText, signInRequest.getKaptcha())) {
            throw new AuthException(ErrorCode.USER_KAPTCHA_ERROR);
        }

        AuthResponse authResponse = userService.login(signInRequest);

        // 清除图片验证码的缓存
        cacheService.remove(CommonConstant.KAPTCHA_CACHE, signInRequest.getCaptchaKey());
        return authResponse;
    }

    /**
     * 退出
     * @return logout success
     */
    @PostMapping("/logout")
    public Result logout() {
        userService.logout();
        return new Result().success();
    }

    /**
     * 生成图片验证码
     *
     * @param captchaKey 图片验证码缓存key
     */
    @GetMapping("/kaptcha")
    public void getKaptchaImage(@RequestParam(name = "t", required = false) String captchaKey) {
        HttpServletResponse response = RequestContextHolderUtil.getResponse();
        response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("image/jpeg");

        // create the text for the image
        String capText = captchaProducer.createText();

        // store the text in the session
        cacheService.set(CommonConstant.KAPTCHA_CACHE, captchaKey, capText);
        try (ServletOutputStream sout = response.getOutputStream()) {
            BufferedImage bi = captchaProducer.createImage(capText);
            ImageIO.write(bi, "jpg", sout);
            sout.flush();
        } catch (IOException e) {
            log.error("wirte KaptchaImage error!", e);
        }
    }

    /**
     * 上传文件
     */
    @PostMapping("/upload")
    public UpFile upload(@RequestPart("file") MultipartFile uploadFile,
                         @RequestParam(value = "fileType", defaultValue = "1", required = false) Integer fileType) {
        // 校验上传文件
        if (uploadFile.isEmpty() || StringUtils.isBlank(uploadFile.getOriginalFilename())) {
            throw new FileOperationException(ErrorCode.UPFILE_IS_EMPTY);
        }
        // 校验文件格式
        String originName = uploadFile.getOriginalFilename();
        String contentType = originName.substring(originName.lastIndexOf(CommonConstant.DOT) + CommonConstant.NUM_1);
        if (!fileProperties.isLegalFile(originName)) {
            throw new FileOperationException(ErrorCode.NOT_SUPPORT_FILE_EXTENSION);
        }

        // 上传文件
        String fileName = FileUtil.saveFile(uploadFile, fileProperties.getUploadPath());
        if (StringUtils.isEmpty(fileName)) {
            throw new FileOperationException(ErrorCode.UPFILE_SAVE_FAIL);
        }

        // 入库
        UpFile upFile = new UpFile();
        upFile.setOriginName(originName);
        upFile.setFileName(fileName);
        upFile.setFileSuffix(contentType);
        upFile.setFileType(fileType);
        // 文件大小，单位：MB
        double fileSize = new BigDecimal(uploadFile.getSize()).divide(new BigDecimal(CommonConstant.NUM_1024))
                .setScale(CommonConstant.NUM_2, BigDecimal.ROUND_HALF_UP).doubleValue();
        upFile.setFileSize(fileSize);
        upFile.setGmtCreate(new Date());
        upFileMapper.insert(upFile);

        return upFile;
    }

    /**
     * 下载模板文件
     *
     * @param fileKey 配置文件中的模板文件的fileKey
     * @return ResponseEntity
     */
    @GetMapping("/template/file")
    public ResponseEntity template(@RequestParam String fileKey) {
        String fileName = MapUtils.getString(fileProperties.getTemplateFileMap(), fileKey);
        if (StringUtils.isEmpty(fileName)) {
            log.error("not found template file! fileKey: {}", fileKey);
            throw new ServiceException(ErrorCode.FILE_NOT_FOUND);
        }

        InputStream is;
        String filePath = fileProperties.getTemplatePath();

        String encodeFileName;
        byte[] body;
        try {
            encodeFileName = new String(fileName.getBytes(), "ISO8859-1");
            File file = new File(filePath + File.separatorChar + fileName);
            is = new FileInputStream(file);
            body = new byte[is.available()];
            int readTotal = is.read(body);
            log.debug("downlaod template,read file total: {}", readTotal);
        } catch (UnsupportedEncodingException e) {
            log.error("file[{}] not found!", fileName, e.getMessage());
            throw new ServiceException(ErrorCode.FILE_CONNOT_DOWNLOAD);
        } catch (IOException e) {
            log.error("file[{}/{}] not found!", fileProperties.getUploadPath(), fileName, e.getMessage());
            throw new ServiceException(ErrorCode.FILE_NOT_FOUND);
        }

        return new ResponseEntity<>(body, buildHeader(encodeFileName), HttpStatus.OK);
    }

    private HttpHeaders buildHeader(String encodeFileName) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Disposition", String.format("attachment; filename=\"%s\"", encodeFileName));
        headers.setCacheControl("no-cache, no-store, must-revalidate");
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");

        return headers;
    }

    /**
     * 查看图片文件，返回base64
     *
     * @param imageFileName 文件名称
     * @return 图片base64编码字符串
     */
    @GetMapping("/viewimg")
    public String viewimg(@RequestParam(name = "image") String imageFileName) {
        // 图片文件，转换成base64返回，使用文件配置类的默认上传路径
        try {
            File imgFile = new File(fileProperties.getUploadPath() + File.separatorChar + imageFileName);
            InputStream is = new FileInputStream(imgFile);
            byte[] data = new byte[is.available()];
            is.read(data);
            is.close();
            BASE64Encoder encoder = new BASE64Encoder();
            String base64Str = encoder.encode(data);
            // 删除 \r\n
            base64Str = base64Str.replaceAll("\n", "").replaceAll("\r", "");

            // 构建base64图片头
            String formatter = "data:image/%s;base64,%s";
            String fileSuffix = imageFileName.substring(
                    imageFileName.lastIndexOf(CommonConstant.DOT) + CommonConstant.NUM_1);

            return String.format(formatter, fileSuffix, base64Str);
        } catch (IOException e) {
            log.error("file[{}/{}] not found!", fileProperties.getUploadPath(), imageFileName, e);
            throw new ServiceException(ErrorCode.FILE_NOT_FOUND);
        }
    }

    /**
     * 以流的形式返回图片
     *
     * @param imageName 图片名称
     */
    @GetMapping("/imgview")
    public void viewImg(@RequestParam(name = "image") String imageName) {
        File imgFile = new File(fileProperties.getUploadPath() + File.separatorChar + imageName);

        HttpServletResponse response = RequestContextHolderUtil.getResponse();
        response.setContentType("multipart/form-data");
        try (InputStream is = new FileInputStream(imgFile); OutputStream out = response.getOutputStream()) {
            IOUtils.copy(is, out);
            out.flush();
        } catch (IOException e) {
            log.error("file[{}/{}] not found!", fileProperties.getUploadPath(), imageName, e);
            throw new ServiceException(ErrorCode.FILE_NOT_FOUND);
        }
    }

    /**
     * 下载文件
     *
     * @param fileName 文件名称
     * @return ResponseEntity
     */
    @GetMapping("/download")
    public ResponseEntity<byte[]> downloadFile(@RequestParam String fileName) {
        String encodeFileName;
        InputStream is;
        byte[] body;
        try {
            encodeFileName = new String(fileName.getBytes(), "ISO8859-1");
            File file = new File(fileProperties.getUploadPath() + File.separatorChar + fileName);
            is = new FileInputStream(file);
            body = new byte[is.available()];
            int readTotal = is.read(body);
            log.debug("downlaod file, read file total: {}", readTotal);
        } catch (UnsupportedEncodingException e) {
            log.error("file[{}] not found!", fileName, e);
            throw new ServiceException(ErrorCode.FILE_CONNOT_DOWNLOAD);
        } catch (IOException e) {
            log.error("file[{}/{}] not found!", fileProperties.getUploadPath(), fileName, e);
            throw new ServiceException(ErrorCode.FILE_NOT_FOUND);
        }

        return new ResponseEntity<>(body, buildHeader(encodeFileName), HttpStatus.OK);
    }
}